home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Games / Hugo / Library / objlib.h < prev    next >
Text File  |  1997-05-02  |  45KB  |  1,947 lines

  1. !----------------------------------------------------------------------------
  2. !
  3. !          HUGO Object Library v2.3.2 by Kent Tessman (c) 1995-1997
  4. !                      for use with Hugo Compiler v2.3
  5. !
  6. !----------------------------------------------------------------------------
  7. !        
  8. !       The Hugo Object Library contains the following useful class 
  9. !       definitions:
  10. !
  11. !               room
  12. !               character, female_character
  13. !               door
  14. !               direction (including direction objects)
  15. !               scenery
  16. !               component
  17. !
  18. !               vehicle
  19. !               plural_class, identical_class
  20. !               attachable
  21. !
  22. !       (NOTE:  In order to use the vehicle class, plural_class or 
  23. !       identical_class, or the attachable class, the flags USE_VEHICLES,
  24. !       USE_PLURAL_OBJECTS, or USE_ATTACHABLES, respectively, must be set.)
  25. !
  26. !----------------------------------------------------------------------------
  27.  
  28. #version 2.3
  29.  
  30. #ifclear _COMPILING_HUGOLIB
  31. #message warning "OBJLIB.H not included separately from HUGOLIB.H"
  32. #endif
  33.  
  34. #ifset _COMPILING_HUGOLIB
  35.  
  36. ! BASIC ROOM:
  37.  
  38. class room "room_class"
  39. {
  40.     type room
  41.     is static, light, open
  42. }
  43.  
  44.  
  45. ! STANDARD CHARACTERS:
  46.  
  47. class character "character_class"
  48. {
  49.     type character
  50.     pronouns "he", "him", "his", "himself"
  51.     capacity 50
  52.     holding 0
  53.     is living, transparent, static
  54. }
  55.  
  56. character female_character "female_character_class"
  57. {
  58.     type female_character
  59.     pronouns "she", "her", "her", "herself"
  60.     is female
  61. }
  62.  
  63.  
  64. ! DOORS:
  65. !
  66. ! This door object assumes that the object will ultimately have 
  67. ! exactly two components to the between property:  one on either 
  68. ! side of the door.
  69.  
  70. property between alias found_in
  71.  
  72. class door "door"
  73. {
  74.     type door
  75.     door_to                            ! depends on actor location
  76.     {
  77.         local currentroom
  78.         if actor = player
  79.             currentroom = location
  80.         else
  81.             currentroom = parent(actor)
  82.             
  83.         ! sitting on or in something
  84. #ifclear USE_VEHICLES
  85.         if actor = player and player not in location
  86. #endif
  87. #ifset USE_VEHICLES
  88.         if actor = player and player not in location and
  89.             parent(player).type ~= vehicle:
  90. #endif
  91.         {
  92.             ! "You'll have to get out first..."
  93.             OMessage(door, 1)
  94.             return true
  95.         }
  96.  
  97.         if self is not open and actor = player
  98.         {
  99. #ifclear NO_VERBS
  100.             if verbroutine = &DoGo and self is not locked and
  101.                 self is openable:
  102.             {
  103.                 OMessage(door, 2)       ! "(opening it first)"
  104.                 Perform(&DoOpen, self)
  105.                 main    ! counts as a turn
  106.                 if self is not open
  107.                     return false
  108.             }
  109.             elseif verbroutine = &DoGo and self is lockable
  110.                 {OMessage(door, 3)      ! "It is locked."
  111.                 return true}
  112.             else
  113.             {
  114. #endif
  115.                 OMessage(door, 4)       ! "It is closed."
  116.                 return true
  117. #ifclear NO_VERBS
  118.             }
  119. #endif
  120.         }
  121.  
  122.         if self is not open and (actor in location or 
  123.             location = self.between #((parent(actor) = \
  124.                 self.between #1) + 1))
  125.         {
  126.             ! Notify the player that the player or a character
  127.             ! has gone through the door:
  128.             !
  129.             OMessage(door, 5)       
  130.             general = 2
  131.         }
  132.  
  133.         if currentroom = self.between #1
  134.             return self.between #2
  135.         else
  136.             return self.between #1
  137.     }
  138.     long_desc
  139.     {
  140.         if self is open
  141.             OMessage(door, 6)       ! when open
  142.         else
  143.             OMessage(door, 7)       ! when closed
  144.     }
  145.     is openable, static
  146. }
  147.  
  148.  
  149. ! COMPASS DIRECTIONS:
  150.  
  151. property dir_to alias capacity          ! for directions only
  152. property dir_from alias size            !
  153.  
  154. class direction "direction_class"
  155. {
  156.     type direction
  157.     article "the"
  158.     found_in
  159.     {
  160.         if self in direction
  161.             return location
  162.     }
  163.     is static, known
  164. }
  165.  
  166. direction n_obj "north"
  167.     {in direction
  168.     nouns "n", "north"
  169.     dir_to n_to
  170.     dir_from s_obj}
  171.  
  172. direction ne_obj "northeast"
  173.     {in direction
  174.     nouns "ne", "northeast"
  175.     dir_to ne_to
  176.     dir_from sw_obj}
  177.  
  178. direction e_obj "east"
  179.     {in direction
  180.     nouns "e", "east"
  181.     dir_to e_to
  182.     dir_from w_obj}
  183.  
  184. direction se_obj "southeast"
  185.     {in direction
  186.     nouns "se", "southeast"
  187.     dir_to se_to
  188.     dir_from nw_obj}
  189.  
  190. direction s_obj "south"
  191.     {in direction
  192.     nouns "s", "south"
  193.     dir_to s_to
  194.     dir_from n_obj}
  195.  
  196. direction sw_obj "southwest"
  197.     {in direction
  198.     nouns "sw", "southwest"
  199.     dir_to sw_to
  200.     dir_from ne_obj}
  201.  
  202. direction w_obj "west"
  203.     {in direction
  204.     nouns "w", "west"
  205.     dir_to w_to
  206.     dir_from e_obj}
  207.  
  208. direction nw_obj "northwest"
  209.     {in direction
  210.     nouns "nw", "northwest"
  211.     dir_to nw_to
  212.     dir_from se_obj}
  213.  
  214. direction u_obj "above"
  215.     {in direction
  216.     nouns "u", "up"
  217.     article ""
  218.     dir_to u_to
  219.     dir_from d_obj}
  220.  
  221. direction d_obj "below"
  222.     {in direction
  223.     nouns "d", "down"
  224.     article ""
  225.     dir_to d_to
  226.     dir_from u_obj}
  227.  
  228. direction in_obj "in"
  229.     {in direction
  230.     noun "in", "inside"
  231.     dir_to in_to
  232.     dir_from out_obj}
  233.  
  234. direction out_obj "out"
  235.     {in direction
  236.     noun "out", "outside"
  237.     dir_to out_to
  238.     dir_from in_obj}
  239.  
  240.  
  241. ! SCENERY: 
  242. !
  243. ! Scenery objects are accessible to actions, but are not individually listed 
  244. ! as part of the room contents (although their children are).  They are 
  245. ! intended to be mentioned in the room description, and still require 
  246. ! explicit names, articles, nouns, and adjectives.  The type property
  247. ! of a scenery object should never be changed.
  248.  
  249. class scenery "scenery_class"
  250. {
  251.     type scenery
  252.     exclude_from_all
  253.     {
  254.         if verbroutine = &DoGet
  255.             return true
  256.         else
  257.             return false
  258.     }
  259.     is static, hidden
  260. }
  261.  
  262.  
  263. ! COMPONENT:
  264. !
  265. ! Components are essentially movable scenery objects, which are considered
  266. ! to be attached to another object.  The part_of property is initialized
  267. ! with the object of which the new object is a component.  Note that 
  268. ! children of components are not listed in the room description; their
  269. ! children are, however, listed when directly examining the object.
  270.  
  271. property part_of alias found_in
  272.  
  273. scenery component "component_class"
  274. {
  275.     type component
  276.     part_of nothing
  277.     before
  278.     {
  279.         object DoGet
  280.         {
  281.             OMessage(component)     ! "You can't separate that..."
  282.         }
  283.     }
  284. }
  285.  
  286.  
  287. ! VEHICLE:
  288. !
  289. ! This vehicle object expects the object's vehicle_move property routine 
  290. ! to return true if the vehicle is presently capable of movement; if it 
  291. ! isn't, vehicle_move is responsible for printing an appropriate message
  292. ! before returning false.
  293. !
  294. ! The vehicle_verbs property contains a list of the valid verbs for vehicle 
  295. ! movement, while the vehicle_path property contains a list of all the 
  296. ! vehicles which may travel in a particular room object.
  297. !
  298. ! The DoMoveinVehicle routine may conceivably be used by all basic vehicles
  299. ! in the game.  However, new grammar incorporating the vehicle movement verbs
  300. ! for each object (corresponding exactly to the list under vehicle_verbs)
  301. ! must be specified, as in:
  302. !
  303. !       verb "<verb1>"[, "<verb2>",...]
  304. !               *                       DoMoveinVehicle
  305. !               * object                DoMoveinVehicle
  306. !               ...
  307. !
  308. ! For different vehicle objects, the verbs (and syntax) may change, but
  309. ! the call to DoMoveinVehicle remains the same.
  310. !
  311. ! Because of the relative complexity of the vehicle object, it must be
  312. ! explicitly included by setting the USE_VEHICLES flag.
  313.  
  314. #ifset USE_VEHICLES
  315.  
  316. property vehicle_move                   ! for vehicles only
  317. property vehicle_verbs                  !  "     "      "
  318. property vehicle_verb alias vehicle_verbs
  319.  
  320. property vehicle_path                   ! for rooms only
  321.  
  322. class vehicle "vehicle_class"
  323. {
  324.     type vehicle
  325.     vehicle_verb "drive"            ! default verb
  326.     prep "in", "out"                !   "     prepositions
  327.     vehicle_move true               ! by default, always ready to move
  328.     before
  329.     {
  330.         parent(player) DoGo
  331.         {
  332.             ! "To walk, you'll have to get out..."
  333.             OMessage(vehicle, 1, self)
  334.         }
  335.     }
  336.     is enterable, static
  337. }
  338.  
  339. routine DoMoveinVehicle
  340. {
  341.     local v, moveto
  342.  
  343.     if player in location
  344.     {
  345.         OMessage(vehicle, 2)    ! "You aren't in anything."
  346.         return false
  347.     }
  348.  
  349.     v = parent(player)              ! the vehicle
  350.     if v.before:  return true
  351.  
  352.     ! Match the verb
  353.     if not InList(v, vehicle_verbs, word[1])
  354.         {OMessage(vehicle, 3, v)        ! wrong verb for this vehicle
  355.         return false}
  356.     
  357.     if not object or (object = v and player in object)
  358.         {OMessage(vehicle, 4)   ! "Specify a direction as well..."
  359.         return false}
  360.  
  361.     if not v.vehicle_move:  return true
  362.  
  363.     if obstacle
  364.     {
  365.         OMessage(vehicle, 5, v) ! "X stops you from moving."
  366.         return true
  367.     }
  368.  
  369.     if object.type ~= direction
  370.     {
  371.         moveto = object.door_to
  372.         if not moveto
  373.             ! "You can't (drive) in there..."
  374.             OMessage(vehicle, 6, v)
  375.  
  376.         if moveto <= 1
  377.             return
  378.     }
  379.     else
  380.         moveto = location.(object.dir_to)
  381.  
  382.     if not InList(moveto, vehicle_path, v) and moveto ~= 1
  383.     {
  384.         OMessage(vehicle, 7, v) ! "You can't (drive) that way."
  385.         return false
  386.     }
  387.     elseif moveto = 0
  388.     {
  389.         if not location.cant_go
  390.             OMessage(vehicle, 7, v)
  391.         return false
  392.     }
  393.     elseif moveto = 1               ! already printed message
  394.         return false            !   (moveto is never 1)
  395.     
  396. #ifset USE_ATTACHABLES
  397.     if ObjectisAttached(v, location, moveto)
  398.         return false
  399. #endif
  400.     
  401.     ! Finally, the vehicle can move
  402.     move v to moveto
  403.     v is moved
  404.     old_location = location
  405.     location = moveto
  406.  
  407. #ifset USE_ATTACHABLES
  408.     MoveAllAttachables(v, old_location, location)
  409. #endif
  410.     
  411.     if not FindLight(location)
  412.         DarkWarning
  413.     else
  414.         {DescribePlace(location)
  415.         location is visited}
  416.     
  417.     run parent(player).after
  418.     
  419.     return true
  420. }
  421.  
  422. #endif  ! ifset USE_VEHICLES
  423.  
  424.  
  425. ! PLURAL OBJECTS:
  426. !
  427. ! (Based on an implementation proposed by Jim Newland.)
  428. !
  429. ! Plural objects are unique objects that can be referred to by a common
  430. ! group name, such as "books" for a dictionary and a cookbook.  Identical
  431. ! objects are similar, except that the case is one of, for example, "coins"
  432. ! for two identical coins.
  433. !
  434. ! The plural_of property contains a list of all objects belonging to the
  435. ! given plural_class (or identical_class).  The property single_noun holds
  436. ! the singular form(s) of the object name, i.e. "book" or "coin".
  437. !
  438. ! Member objects are defined simply as objects, without using the class
  439. ! name in the declaration.  For identical objects, the identical_to
  440. ! property must be defined, giving the name of the identical_class to
  441. ! which the object belongs.  As well, the object in any action performed
  442. ! on an identical object is generally assumed to be the identical_class.
  443. ! That is, properties such as long_desc should be defined for the
  444. ! identical_class.
  445. !
  446. ! Words in the adjectives property of the member objects should also be
  447. ! adjectives for the given plural or identical class.
  448. !
  449. ! (NOTE:  Members of a plural_class may have plural_is (essentially
  450. ! identical_to for plural objects), but it is optional and used only for 
  451. ! object listing.)
  452. !
  453. ! The plural_verbs property should return true if the current verbroutine
  454. ! is in the list of valid actions for the class in question.
  455. !
  456. ! The Init routine must contain the line:  InitPluralObjects
  457.  
  458. #ifset USE_PLURAL_OBJECTS
  459.  
  460. global plural_count             ! number of plural/identical classes used
  461. constant MAX_PLURALS 32         ! maximum number of classes
  462. array plurals[MAX_PLURALS]
  463.  
  464. property plural_of              ! contains a list of all individual objects
  465. property single_noun            ! i.e. "thing" vs. "things"
  466. property identical_to           ! for member objs., set to the 
  467.                 !   identical_class to which they belong
  468. property plural_is alias identical_to
  469.  
  470. property plural_verbs           ! returns true for valid verbroutines
  471.  
  472. ! Each plural or identical object must have a property giving the total
  473. ! number specified:
  474. property pluralobj_number alias nw_to
  475.  
  476. global pluralobj_heldmode       ! 1, -1, 0 for held, notheld, or neither
  477.  
  478. class plural_class "plural_class"
  479. {
  480.     type plural_class
  481.     in_scope 0                      ! may be an actor at some point
  482.     size 0                          ! must be weightless
  483.     pluralobj_number 0
  484.     plural_verbs
  485.     {
  486.         if verbroutine = &DoLook, &DoGet, &DoDrop, &DoPutin
  487.             return true
  488.         else:  return false
  489.     }
  490.     before
  491.     {
  492.         object                  ! intercepts all plural_object verbs
  493.         {
  494.             local i, j, n, obj, count, flag
  495.  
  496. ! Count the number of workflagged member objects, to see if there are 
  497. ! less than specified (or any at all, for that matter).
  498.  
  499.             for (i=1; i<=self.#plural_of; i++)
  500.             {
  501.                 if self.plural_of #i is workflag
  502.                 {
  503.                     obj = self.plural_of #i
  504.                     AssignPronoun(obj)
  505.                     n++
  506.                 }
  507.             }
  508.  
  509.             if n > 1:  AssignPronoun(self)
  510.  
  511.             if n = 0
  512.             {
  513.                 ! "There are none..."
  514.                 OMessage(plural_class, 1)
  515.                 return true
  516.             }
  517.             
  518.             if (n < self.pluralobj_number) or
  519.                 (n = 1 and self.pluralobj_number = 0):
  520.             {
  521.                 ! "There are only x things..."
  522.                 OMessage(plural_class, 2, n)
  523.                 return true
  524.             }
  525.  
  526. ! If it's not an allowable plural-objects action, disqualify it:
  527.  
  528.             if self.pluralobj_number ~=1 and not self.plural_verbs
  529.             {
  530.                 ! "You'll have to do that one at a time."
  531.                 OMessage(plural_class, 3)
  532.                 return true
  533.             }
  534.  
  535. ! Handle examining objects as a group specially:
  536.  
  537.             if verbroutine=&DoLook
  538.             {
  539.                 if self.type = identical_class
  540.                     return false
  541.  
  542.                 if self.long_desc
  543.                     return true
  544.  
  545.                 flag = 0
  546.                 for (i=1; i<=self.#plural_of; i++)
  547.                 {
  548.                     j = self.plural_of #i
  549.                     if &j.long_desc
  550.                         flag = true
  551.                 }
  552.                 if flag = 0:  return false
  553.             }
  554.                 
  555. ! Finally, process the action on workflagged individual objects:
  556.  
  557.             for (i=1; i<=self.#plural_of; i++)
  558.             {
  559.                 obj = self.plural_of #i
  560.                 if obj is workflag
  561.                 {
  562.                     if n~=1 and self.pluralobj_number~=1
  563.                     {
  564.                       if self.type = identical_class and
  565.                         obj.article:
  566.                           {print CountWord(count+1);
  567.                           print " ";}
  568.                       print obj.name; ":  ";
  569.                     }
  570.  
  571.                     Perform(verbroutine, obj, xobject)
  572.                     count++
  573.  
  574.                     AssignPronoun(obj)
  575.                 }
  576.  
  577.                 ! number of objects specified (if specified)
  578.                 if count=self.pluralobj_number and count>0
  579.                     break
  580.             }
  581.         }
  582.     }
  583.     is plural, known
  584. }
  585.  
  586. plural_class identical_class "identical_class"
  587. {
  588.     type identical_class
  589. }
  590.  
  591.  
  592. ! InitPluralObjects must be called in the Init routine in order for plural
  593. ! or identical objects to be used.
  594.  
  595. routine InitPluralObjects
  596. {
  597.     local i, count
  598.  
  599.     for (i=1; i<=objects; i++)
  600.     {
  601.         if (i.type = plural_class or i.type = identical_class) and
  602.             i ~= plural_class and i~=identical_class
  603.         {
  604.             plurals[count] = i
  605.             if ++count >= MAX_PLURALS
  606.             {
  607.                 "[WARNING:  Maximum number of plural/identical
  608.                 classes exceeded.  Increase MAX_PLURALS in
  609.                 the Hugo Object Library.]"
  610.                 return
  611.             }
  612.         }
  613.     }
  614.     plural_count = count
  615. }
  616.  
  617.  
  618. ! ParsePluralObjects is called by the Parse routine.
  619.  
  620. routine ParsePluralObjects
  621. {
  622.     local i, j, k, n, w, wn
  623.     local any, plist, pobj, plural_type, loc, xobj, pobj_number
  624.     local 2ndpass
  625.  
  626. ! Before doing anything, move all plural/identical object classes back to 
  627. ! their respective holding classes:
  628.  
  629.     for (i=0; i<plural_count; i++)
  630.         move plurals[i] to plurals[i].type
  631.     
  632.     if actor = 0:  actor = player
  633.     
  634.     pluralobj_heldmode = 0  ! i.e. not explicitly held or not
  635.     wn = 2                  ! starting word number
  636.  
  637. :ScanforPluralWords
  638.  
  639.     pobj_number = 0         ! default explicit count (pluralobj_number)
  640.     
  641. ! The first step is to scan the phrase for a potential plural object word:
  642.     
  643.     for (w=wn; w<=words; w++)
  644.     {
  645.         if word[w] = 0 or w>words
  646.             break           ! end of phrase
  647.  
  648.         if word[w] = "my"
  649.         {
  650.             pluralobj_heldmode = 1
  651.         }
  652.         
  653.         pobj = 0
  654.         for plist in plural_class
  655.         {
  656.             if InList(plist, single_noun, word[w])
  657.                 {pobj = plist
  658.                 plural_type = single_noun
  659.                 break}
  660.  
  661.             if InList(plist, noun, word[w])
  662.             {
  663.                 pobj = plist
  664.                 plural_type = noun
  665.                 break
  666.             }
  667.         }
  668.         if pobj:  break
  669.         
  670.         for plist in identical_class
  671.         {
  672.             pobj = 0
  673.  
  674.             if InList(plist, single_noun, word[w])
  675.             {
  676.                 pobj = plist
  677.                 pobj_number = 1
  678.                 plural_type = single_noun
  679.                 break
  680.             }
  681.  
  682.             if InList(plist, noun, word[w])
  683.             {
  684.                 pobj = plist
  685.                 plural_type = noun
  686.                 break
  687.             }
  688.         }
  689.         if pobj:  break
  690.     }
  691.     if not pobj:  return false      ! didn't find anything
  692.  
  693. ! At this point, <w> points to the word and <pobj> is the object number of 
  694. ! the plural class.  <plural_type> is either noun or single_noun.  What 
  695. ! follows is the actual parsing of the plural structure:
  696.  
  697. ! Determine (roughly) if object is explicitly held or notheld; the
  698. ! VerbHeldmode routine checks to see if the verb necessarily implies a
  699. ! specific heldmode by checking the first 6 words--i.e., even if the first 
  700. ! five represent an object name in an actor-directed command.
  701.  
  702.     if not 2ndpass
  703.     {
  704.       for (i=1; i<=6 and i<=w; i++)
  705.       {
  706.         if pluralobj_heldmode:  break
  707.  
  708.         if word[i] = "":  break         ! end of phrase
  709.  
  710.         VerbHeldmode(word[i])
  711.  
  712.       }
  713.  
  714. ! If it is a single_noun (i.e. "any thing" or "either thing"), the object 
  715. ! global will have to be appropriately determined.  <pobj> will hold the 
  716. ! selected object (if any).  In any case, it will be necessary to know if 
  717. ! at least one object (in the case of multiple plural object specification) 
  718. ! is available, in order to move the plural_class into scope.
  719.  
  720. ! To boot, it could also get ugly if a container or platform xobject is 
  721. ! specified as the location for the object(s) being acted upon.  There is 
  722. ! no polite way of figuring out what the xobject might be at this point.
  723.  
  724.       for (i=w; i<words; i++)
  725.       {
  726.         k = word[i]             ! for shorthand purposes only
  727.         if k = 0:  break        ! end of phrase
  728.         
  729.         if (k = "from", "outof", "offof", "off") or
  730.             (pluralobj_heldmode ~= 1 and 
  731.             (k = "in" or k = "inside" or k = "on")):
  732.         {
  733.             xobj = 0        ! nothing selected yet
  734.             do
  735.             {
  736.                 i=i+1   ! get next word
  737.  
  738.                 for (j=1; j<=pobj.#plural_of; j++)
  739.                 {
  740.                     n = parent(pobj.plural_of #j)
  741.                     if ObjWord(word[i], n)
  742.                         xobj = n
  743.                 }
  744.             }
  745.             while i<=words and word[i]~=0
  746.  
  747.             break
  748.         }
  749.       }
  750.  
  751.       n = 0
  752.       if xobj
  753.         loc = xobj
  754.       elseif actor = player
  755.         loc = location
  756.       else 
  757.         loc = parent(actor)
  758.     }
  759.  
  760. ! Remove the old plural_class adjective(s), if any
  761.  
  762.     while w>1 and (ObjWord(word[w-1], pobj)=adjective or word[w-1]="my")
  763.     {
  764.         DeleteWord(w-1)
  765.         w--
  766.     }
  767.  
  768. ! Then, find out if there is a preceding number
  769.  
  770.     if w > 1
  771.     {
  772.         k = WordisNumber(word[w-1])
  773.         if k >= 1                       ! e.g., "one (or more) things"
  774.         {
  775.             DeleteWord(w-1)
  776.             w--
  777.             pobj_number = k
  778.         }
  779.  
  780.         if WordisNumber(word[w-1])>=1   ! "two of the three"
  781.         {
  782.             pobj_number = WordisNumber(word[w-1])
  783.             DeleteWord(w-1)
  784.             w--
  785.         }
  786.     }
  787.  
  788. ! Remove a preceding "all" or "any"; i.e. "all things" is the same as "things"
  789.  
  790.     if w>1 and (word[w-1] = "~all" or word[w-1] = "~any")
  791.     {
  792.         if word[w-1] = "~any":  any = true
  793.         DeleteWord(w-1)
  794.         w--
  795.     }
  796.  
  797. ! Now see if the object (or a member of the plural_class) is available given 
  798. ! the restrictions of pluralobj_heldmode:
  799.  
  800.     n = 0
  801.     j = 0
  802.  
  803.     if pluralobj_heldmode = -1              ! explicitly notheld object
  804.     {
  805.         for (i=1; i<=pobj.#plural_of; i++)
  806.         {
  807.             k = pobj.plural_of #i
  808.             k is not workflag
  809.  
  810.             if k in loc
  811.             {
  812.                 n = k
  813.                 k is workflag
  814.                 j++
  815.             }
  816.             if n and plural_type = single_noun:  break
  817.         }
  818.     }
  819.  
  820.     if not n or pobj_number = 0 or j < pobj_number
  821.     {
  822.         for (i=1; i<=pobj.#plural_of; i++)
  823.         {
  824.             k = pobj.plural_of #i
  825.  
  826.             ! explicitly notheld object
  827.             if pluralobj_heldmode = -1 and k is not workflag
  828.             {
  829.                 if FindObject(k, loc) and k not in actor
  830.                 {
  831.                     n = k
  832.                     k is workflag
  833.                     j++
  834.                 }
  835.             }
  836.             elseif pluralobj_heldmode ~= -1
  837.                 k is not workflag
  838.  
  839.             ! explicitly held object
  840.             if pluralobj_heldmode = 1
  841.             {
  842.                 if k in actor
  843.                 {
  844.                     n = k
  845.                     k is workflag
  846.                     j++
  847.                 }
  848.             }
  849.  
  850.             ! or neither
  851.             elseif pluralobj_heldmode = 0
  852.             {
  853.                 if FindObject(k, loc)
  854.                 {
  855.                     n = k
  856.                     k is workflag
  857.                     j++
  858.                 }
  859.             }
  860.  
  861.             if n and plural_type = single_noun:  break
  862.             if j and j = pobj_number:  break
  863.         }
  864.     }
  865.  
  866. ! Even if no object is found, it will be necessary to insert one in the 
  867. ! input line to generate a "You don't see that"-esque parser message in 
  868. ! the case of a single_noun specification.
  869.  
  870.     if plural_type = single_noun and not n
  871.         n = pobj.plural_of #1
  872.  
  873. ! If it's not a plural specification--i.e. it is an "any" or "either" 
  874. ! phrasing instead--change the plural object word to represent <n>.
  875.  
  876.     i = words
  877.  
  878.     if plural_type = single_noun and pobj.type ~= identical_class and any
  879.     {
  880.         SetObjWord(w, n)
  881.         if pobj.type = plural_class
  882.             print "("; The(n); ")"
  883.     }
  884.  
  885. ! If it is a plural specification--plural_class.noun instead of single_noun--
  886. ! move the plural class into an accessible position, but only if one of 
  887. ! the component members is available.  The same goes for identical_classes.
  888.  
  889.     elseif n and (plural_type = noun or pobj.type = identical_class) 
  890.     {
  891.         ! Insert the name of the identical_class, if necessary
  892.         if pobj.type = identical_class
  893.             SetObjWord(w, pobj)
  894.  
  895.         if pluralobj_heldmode = 1       ! in inventory
  896.             move pobj to actor
  897.         elseif xobj                     ! in or on something
  898.             move pobj to xobj
  899.         else                            ! anything else
  900.             PutinScope(pobj, actor)
  901.     }
  902.  
  903.     pobj.pluralobj_number = pobj_number     ! # of plural objects
  904.  
  905.     wn = w + pobj.#adjectives + (pobj.noun > 0)
  906.     if wn < i
  907.     {
  908.         pobj = 0
  909.         2ndpass = true
  910.         jump ScanforPluralWords
  911.     }
  912.  
  913.     return true
  914. }
  915.  
  916.  
  917. ! VerbHeldmode sets the pluralobj_heldmode global to the appropriate value
  918. ! 1, -1, or 0, based on the verb being used; returns true if the verb
  919. ! indicates a particular heldmode.  This routine can be replaced in order 
  920. ! to change the selection of verbs.  
  921.  
  922. routine VerbHeldmode(a)
  923. {
  924.     if a = "get", "take", "pick", "remove":
  925.         pluralobj_heldmode = -1         ! explicitly notheld
  926.  
  927.     elseif a = "drop", "put", "leave", "place", "throw", 
  928.         "give", "offer":
  929.         pluralobj_heldmode = 1          ! explicitly held
  930.  
  931.     return (pluralobj_heldmode~=0)
  932. }
  933.  
  934.  
  935. ! WordisNumber returns the number given by the specified word (where the
  936. ! argument is, for example, word[x]).  If the word is not a number, or
  937. ! is out of range, -1 is returned.
  938.  
  939. routine WordisNumber(w)
  940. {
  941.     select w
  942.     case "zero", "0":       return 0
  943.     case "one", "1":        return 1
  944.     case "two", "2":        return 2
  945.     case "three", "3":      return 3
  946.     case "four", "4":       return 4
  947.     case "five", "5":       return 5
  948.     case "six", "6":        return 6
  949.     case "seven", "7":      return 7
  950.     case "eight", "8":      return 8
  951.     case "nine", "9":       return 9
  952.     case "ten", "10":       return 10
  953.     case else:              return -1
  954. }
  955.  
  956.  
  957. ! CountWord prints an appropriate article as specified by the value <n>.
  958.  
  959. routine CountWord(n)
  960. {
  961.     select n
  962.     case 1:         return "first"
  963.     case 2:         return "second"
  964.     case 3:         return "third"
  965.     case 4:         return "fourth"
  966.     case 5:         return "fifth"
  967.     case 6:         return "sixth"
  968.     case 7:         return "seventh"
  969.     case 8:         return "eighth"
  970.     case 9:         return "ninth"
  971.     case 10:        return "tenth"
  972.     case else:      return "next"
  973. }
  974.  
  975.  
  976. ! RemoveIdentical removes the object <obj> from its current identical_class;
  977. ! used for instances where, for example, an indistinguishable object is
  978. ! marked or otherwise made unique.
  979.  
  980. routine RemoveIdentical(obj)
  981. {
  982.     local j, o
  983.  
  984.     for (o=0; o<plurals; o++)
  985.     {
  986.         j = InList(plurals[o], plural_of, obj)
  987.         if j                                    ! if it matches
  988.         {
  989.             plurals[o].plural_of #j = 0     ! remove it
  990.             return true
  991.         }
  992.     }
  993.     return false
  994. }
  995.  
  996.  
  997. ! AddIdentical adds (i.e. returns) object <obj> to the identical_class <i>.
  998.  
  999. routine AddIdentical(obj, i)
  1000. {
  1001.     local j
  1002.  
  1003.     for (j=1; j<i.#plural_of; j++)
  1004.     {
  1005.         if i.plural_of #j = 0           ! a blank slot
  1006.         {
  1007.             i.plural_of #j = obj    ! add it
  1008.             return j
  1009.         }
  1010.     }
  1011.     return false
  1012. }
  1013.  
  1014. #endif  ! ifset USE_PLURAL_OBJECTS
  1015.  
  1016.  
  1017. ! ATTACHABLE:
  1018. !
  1019. ! Because ropes, chains, and other such household items are notoriously
  1020. ! hard to code.
  1021. !
  1022. ! The attachable_to property contains a list of all items to which the
  1023. ! object may be attached.  The attached_to property will ultimately contain
  1024. ! the objects to which the object is attached; it must be defined with an
  1025. ! appropriate number of elements.  For example, a rope that can be tied
  1026. ! between two things must have the following in its object definition:
  1027. !
  1028. ! attachable rope "rope"                ! or something similar
  1029. ! {
  1030. !       ...
  1031. !       attached_to 0, 0
  1032. !       ...
  1033. ! }
  1034. !
  1035. ! so that attached_to can hold a total of two object numbers.
  1036. !
  1037. ! A harness that is already attached to a wagon, but which can also be 
  1038. ! attached to six horses (objects) at the same time, might be initialized
  1039. ! as follows:
  1040. !
  1041. !       attached_to wagon, 0, 0, 0, 0, 0, 0
  1042. !
  1043. ! with room for seven elements.
  1044. !
  1045. ! The attach_take and attach_drop properties are normally not set.  If
  1046. ! attach_take is true, an attempt to take the attachable is made before
  1047. ! attaching (or detaching) it.  If attach_drop is true, the object is
  1048. ! automatically dropped after it is attached.
  1049. !
  1050. ! The attach_verbs and detach_verbs properties contain lists of all valid 
  1051. ! verbs to attach or detach the object.  Similarly to the vehicle class's 
  1052. ! DoMoveinVehicle routine, the DoAttachObject and DoDetachObject can be 
  1053. ! used by all basic attachables, with new grammar specified for the object 
  1054. ! (corresponding exactly to the lists under attach_verbs and detach_verbs),
  1055. ! as in:
  1056. !
  1057. !       verb "<verb1>"[, "<verb2>",...]
  1058. !               *                       DoVague
  1059. !               * object                DoAttachObject
  1060. !               * object "to" xobject   DoAttachObject
  1061. !               ...
  1062. !
  1063. ! Note that DoAttachObject expects a second object (the xobject) to be
  1064. ! given.
  1065. !
  1066. ! To check if a particular object is kept immovable by an attachable, 
  1067. ! call ObjectisAttached(<object>); it returns the number of the attachable,
  1068. ! or false if there is none.
  1069. !
  1070. ! AttachObject and DetachObject may be used to directly attach or detach
  1071. ! an attachable object.
  1072. !
  1073. ! Also, any routine that moves the player or player's parent--such as 
  1074. ! MovePlayer or DoMoveinVehicle--should call MoveAllAttachables to 
  1075. ! reconcile the location of attached objects (since they are not necessarily 
  1076. ! connected via the object tree).  Objects with the mobile attribute set 
  1077. ! may be dragged.
  1078. !
  1079. ! Non-attachables may have the attach_immobile property, which governs
  1080. ! whether or not they may be pulled, dragged, etc. by returning 0 when 
  1081. ! freely moveable or 1 (true) if something is keeping it from moving.  In
  1082. ! the second case, attach_immobile is responsible for printing any 
  1083. ! explanatory message.
  1084.  
  1085. #ifset USE_ATTACHABLES
  1086.  
  1087. property attachable_to
  1088. property attached_to
  1089.  
  1090. property attach_prep
  1091. property detach_prep
  1092. property attached_desc
  1093.  
  1094. property attach_shortdesc       ! Describe attachables using these
  1095. property attach_longdesc        ! in place of short_desc and long_desc
  1096.  
  1097. property attach_verb
  1098. property detach_verb
  1099. property attach_verbs alias attach_verb
  1100. property detach_verbs alias detach_verb
  1101.  
  1102. property attach_take            ! Normally not set, these are true if an
  1103. property attach_drop            ! implicit take or drop is required
  1104.  
  1105. property attach_immobile        ! for non-attachables; returns 0 if moveable,
  1106.                 ! 1 if not (after printing explanation)
  1107.  
  1108. class attachable "attachable_class"
  1109. {
  1110.     type attachable
  1111.     attach_verbs "tie", "attach"
  1112.     detach_verbs "untie", "detach"
  1113.     attach_prep "to"
  1114.     detach_prep "from"
  1115.     attached_desc "tied to"
  1116.     attached_to 0           ! 1 default attachment point
  1117.     desc_detail
  1118.     {
  1119.         local i
  1120.  
  1121.         for (i=1; i<=self.#attached_to; i++)
  1122.         {
  1123.             if self.attached_to #i
  1124.             {
  1125.                 print " ("; self.attached_desc; " "; 
  1126.                 ListAttachments(self)
  1127.                 print ")";
  1128.                 break
  1129.             }
  1130.         }
  1131.     }
  1132.     short_desc
  1133.     {
  1134.         local a, i
  1135.  
  1136.         a = 0
  1137.  
  1138.         ! See first if it's attached to anything
  1139.         for (i=1; i<=self.#attached_to; i++)
  1140.         {
  1141.             if self.attached_to #i
  1142.                 a = self.attached_to #i
  1143.                 
  1144.         }
  1145.  
  1146.         if not a                ! not attached to anything
  1147.         {
  1148.             a = self.attach_shortdesc
  1149.             if not a
  1150.                 Message(&DescribePlace, 2, self)
  1151.             return true
  1152.         }
  1153.         else
  1154.         {
  1155.             if self.attach_shortdesc
  1156.             {
  1157.                 print "\_ ";
  1158.                 OMessage(attachable, 3)  ! "Attached to..."
  1159.             }
  1160.             else:  OMessage(attachable, 1)
  1161.         }
  1162.     }
  1163.     long_desc
  1164.     {
  1165.         local a, i, desc
  1166.  
  1167.         a = 0
  1168.         
  1169.         ! See first if it's attached to anything
  1170.         for (i=1; i<=self.#attached_to; i++)
  1171.         {
  1172.             if self.attached_to #i
  1173.                 a = 1
  1174.         }
  1175.  
  1176.         if not self.attach_longdesc
  1177.             OMessage(attachable, 2) ! "You see nothing special..."
  1178.         if a            ! if it's attached to something(s)
  1179.         {
  1180.             print ""
  1181.             OMessage(attachable, 3) ! "Attached to..."
  1182.         }
  1183.     }
  1184.     before
  1185.     {
  1186.         object DoGet
  1187.         {
  1188.             local i, w
  1189.  
  1190.             self is not workflag
  1191.             if self.attach_take or self.attach_drop
  1192.             {
  1193.                 for (i=1; i<=self.#attached_to; i++)
  1194.                 {
  1195.                   if self.attached_to #i and
  1196.                     self.#attached_to > 1:
  1197.                     ! "You'll have to (detach) it..."
  1198.                     {OMessage(attachable, 4)
  1199.                     return true}
  1200.                   elseif self.attached_to #i
  1201.                     ! "(having to (detach) it first)"
  1202.                     {OMessage(attachable, 5, i)
  1203.                     w = word[1]
  1204.                     word[1] = self.detach_verb
  1205.                     xobject = self.attached_to #i
  1206.                     if not DoDetachObject
  1207.                         return true
  1208.                     Main    ! counts as a turn
  1209.                     word[1] = w
  1210.                     self is workflag
  1211.                     return false}
  1212.                 }
  1213.             }
  1214.             return false
  1215.         }
  1216.  
  1217. #ifset VERBSTUBS
  1218.         object DoTie
  1219.             {Perform(&DoAttachObject, object, xobject)}
  1220.         object DoUntie
  1221.             {Perform(&DoDetachObject, object, xobject)}
  1222. #endif
  1223.     }
  1224.     after
  1225.     {
  1226.         object DoGet
  1227.         {
  1228.             if self is not workflag
  1229.                 return false
  1230.         }
  1231.     }                       
  1232. }
  1233.  
  1234. routine DoAttachObject
  1235. {
  1236.     if object.type ~= attachable
  1237.         {OMessage(attachable, 6)        ! "You can't (attach) that."
  1238.         return false}
  1239.  
  1240.     ! Match the verb
  1241.     if not InList(object, attach_verbs, word[1])
  1242.         {OMessage(attachable, 7, object.attach_verb)    ! wrong verb
  1243.         return false}
  1244.  
  1245.     if not xobject
  1246.         {OMessage(attachable, 8)        ! "Be more specific..."
  1247.         return false}
  1248.  
  1249.     if object.attach_take and object not in player
  1250.     {
  1251.         OMessage(attachable, 9)         ! "(taking it first)"
  1252.         Perform(&DoGet, object)
  1253.         if object not in player
  1254.             return false
  1255.         Main                    ! counts as a turn
  1256.     }
  1257.  
  1258.     ! See if the attach-to object is appropriate
  1259.     if not InList(object, attachable_to, xobject)
  1260.     {
  1261.         ! "You can't (attach) it to that."
  1262.         OMessage(attachable, 10)
  1263.         return false
  1264.     }
  1265.  
  1266.     ! Make sure it's not already attached
  1267.     if InList(object, attached_to, xobject)
  1268.     {
  1269.         ! "Except that it is already (attached) to that."
  1270.         OMessage(attachable, 11)
  1271.         return false
  1272.     }
  1273.  
  1274.     if not ObjectAttach(object, xobject)
  1275.     {
  1276.         ! "Except that it is already (attached) to..."
  1277.         OMessage(attachable, 12)
  1278.         return false
  1279.     }
  1280.  
  1281.     if not xobject.after
  1282.     {
  1283.         if not object.after
  1284.             ! "You (attach) it to..."
  1285.             OMessage(attachable, 13)
  1286.     }
  1287.  
  1288.     return true
  1289. }
  1290.  
  1291. routine DoDetachObject
  1292. {
  1293.     local i, j
  1294.  
  1295.     if object.type ~= attachable
  1296.         {OMessage(attachable, 6)        ! "You can't (detach) that."
  1297.         return false}   
  1298.  
  1299.     ! Match the verb
  1300.     if not InList(object, detach_verbs, word[1])
  1301.         {OMessage(attachable, 7, object.detach_verb)    ! wrong verb
  1302.         return false}
  1303.  
  1304.     ! If second object is given, make sure it is attached
  1305.     if xobject and not InList(object, attached_to, xobject)
  1306.     {
  1307.         ! "Except that it is not (attached) to that."
  1308.         OMessage(attachable, 14)
  1309.         return false
  1310.     }
  1311.     elseif not xobject
  1312.     {
  1313.         j = 0
  1314.         for (i=1; i<=object.#attached_to; i++)
  1315.         {
  1316.             if object.attached_to #i
  1317.                 {j++
  1318.                 xobject = object.attached_to #i}
  1319.         }
  1320.         if j = 0
  1321.         {
  1322.             ! "Except that it is not (attached) to anything."
  1323.             OMessage(attachable, 15)
  1324.             return false
  1325.         }
  1326.         elseif j > 1
  1327.         {
  1328.             ! "Be a little more specific..."
  1329.             OMessage(attachable, 16)
  1330.             return false
  1331.         }
  1332.     }
  1333.  
  1334.     if object.attach_take and object not in player and verbroutine~=&DoGet
  1335.     {
  1336.         OMessage(attachable, 9)         ! "(taking it first)"
  1337.         Perform(&DoGet, object)
  1338.         if object not in player
  1339.             return false
  1340.         Main                    ! counts as a turn
  1341.     }
  1342.  
  1343.     ObjectDetach(object, xobject)
  1344.  
  1345.     j = verbroutine
  1346.     verbroutine = &DoDetachObject
  1347.     if not xobject.after
  1348.     {
  1349.         if not object.after
  1350.             ! "You detach it from..."
  1351.             OMessage(attachable, 17)
  1352.     }
  1353.     verbroutine = j
  1354.  
  1355.     return true
  1356. }
  1357.  
  1358.  
  1359. ! Use the routines ObjectAttach and ObjectDetach to attach or detach the 
  1360. ! attachable given by <att> to or from the object specified by <obj>; they
  1361. ! will take care of setting and/or clearing the appropriate properties, 
  1362. ! returning true if successful.
  1363.  
  1364. routine ObjectAttach(att, obj)
  1365. {
  1366.     local i, point
  1367.  
  1368.     if not InList(att, attachable_to, obj):  return false    
  1369.  
  1370.     ! See first if there is an open point
  1371.     for (i=1; i<=att.#attached_to; i++)
  1372.     {
  1373.         if att.attached_to #i = 0
  1374.             {point = i
  1375.             break}
  1376.     }
  1377.     if not point:  return false     ! no open point
  1378.  
  1379.     att.attached_to #point = obj
  1380.  
  1381.     if object.attach_drop
  1382.         move object to location
  1383.  
  1384.     return point
  1385. }
  1386.  
  1387. routine ObjectDetach(att, obj)
  1388. {
  1389.     local n
  1390.  
  1391.     n = InList(att, attached_to, obj)
  1392.     if n
  1393.         att.attached_to #n = 0
  1394.  
  1395.     return true
  1396. }
  1397.  
  1398.  
  1399. ! ObjectisAttached returns true if something in <oldloc> is keeping
  1400. ! <obj> from moving to <newloc>.
  1401.  
  1402. routine ObjectisAttached(obj, oldloc, newloc)
  1403. {
  1404.     local check
  1405.  
  1406. ! Check first the children of the object in question:
  1407.     
  1408.     check = Check_AttachableChildren(obj, newloc)
  1409.     if check:  jump SomethingisAttached
  1410.  
  1411. ! Failing finding any attachments there, use the roundabout method of 
  1412. ! checking the location:
  1413.     
  1414.     check = Check_Attachedto(obj, oldloc, newloc)
  1415.     if check:  jump SomethingisAttached
  1416.     
  1417.     return false                    ! nothing is stopping it
  1418.  
  1419. :SomethingisAttached
  1420.  
  1421.     if check = -1                   ! something already printed
  1422.         return true
  1423.     
  1424. #ifset USE_VEHICLES
  1425.     if verbroutine = &DoGo or verbroutine = &DoMoveinVehicle
  1426. #endif
  1427. #ifclear USE_VEHICLES
  1428.     if verbroutine = &DoGo
  1429. #endif
  1430.     {
  1431.         ! "Not while it is still (attached) to..."
  1432.         OMessage(attachable, 18, check)
  1433.     }
  1434.     else
  1435.     {
  1436.         ! "You can't move whatever is (attached) to it..."
  1437.         OMessage(attachable, 19, check)
  1438.     }
  1439.  
  1440.     return true
  1441. }
  1442.  
  1443.  
  1444. ! Check_Attachable checks all the attachments of <att> for mobility, where
  1445. ! <att> is an attachable.  Returns the number of the immobile object, or 
  1446. ! false if none.
  1447.  
  1448. routine Check_Attachable(att, newloc)
  1449. {
  1450.     local a, b, i
  1451.  
  1452.     for (i=1; i<=att.#attached_to; i++)
  1453.     {
  1454.         a = att.attached_to #i
  1455.  
  1456. #ifset USE_VEHICLES
  1457.         if a.type = vehicle and not InList(newloc, vehicle_path, a)
  1458.         {
  1459.             ! "You can't pull..."
  1460.             OMessage(attachable, 20, a)
  1461.             return -1
  1462.         }
  1463. #endif
  1464.         if a and (a is not mobile and not Contains(a, att))
  1465.         {
  1466.             if a.attach_immobile
  1467.                 return -1
  1468.             return att
  1469.         }
  1470.         
  1471.         if a.#attachable_to
  1472.         {
  1473.             b = Check_Attachable(a, newloc)
  1474.             if b:  return b
  1475.         }
  1476.  
  1477.         if a and child(a) and a ~= parent(att)
  1478.         {
  1479.             b = Check_AttachableChildren(a, newloc)
  1480.             if b:  return b
  1481.         }
  1482.     }
  1483. }
  1484.  
  1485.  
  1486. ! Check_AttachableChildren checks to see what (if anything) in <obj> is
  1487. ! an attachable.
  1488.  
  1489. routine Check_AttachableChildren(obj, newloc)
  1490. {
  1491.     local b, i
  1492.  
  1493.     for i in obj
  1494.     {
  1495.         if i.#attached_to
  1496.         {
  1497.             b = Check_Attachable(i, newloc)
  1498.             if b:  return b
  1499.         }
  1500.         elseif child(i)
  1501.         {
  1502.             b = Check_AttachableChildren(i, newloc)
  1503.             if b:  return b
  1504.         }
  1505.     }
  1506. }
  1507.  
  1508.  
  1509. ! Check_Attachedto checks attachables in <obj>'s location.  Returns the 
  1510. ! number of the first attachable keeping <obj> from moving; otherwise
  1511. ! returns false.
  1512.  
  1513. routine Check_Attachedto(obj, oldloc, newloc)
  1514. {
  1515.     local i, j, k
  1516.  
  1517.     for i in oldloc
  1518.     {
  1519.         for (j=1; j<=i.#attached_to; j++)
  1520.         {
  1521.             k = i.attached_to #j
  1522.             
  1523.             if k = obj or Contains(obj, k):
  1524.             {
  1525. #ifset USE_VEHICLES
  1526.                 if k.type = vehicle and
  1527.                     not InList(newloc, vehicle_path, k):
  1528.                 {
  1529.                     ! "You can't pull..."
  1530.                     OMessage(attachable, 20, k)
  1531.                     return -1
  1532.                 }
  1533. #endif
  1534.             
  1535.                 if k is not mobile and 
  1536.                     k~=obj and not Contains(obj, k) 
  1537.                 {
  1538.                     if k.attach_immobile
  1539.                         return -1
  1540.                     return i
  1541.                 }
  1542.             }
  1543.         }
  1544.         
  1545.         if child(i)
  1546.         {
  1547.             k = Check_Attachedto(obj, child(i))
  1548.             if k
  1549.                 return k
  1550.         }
  1551.     }
  1552. }
  1553.  
  1554.  
  1555. ! MoveAllAttachables moves anything connected (no matter how tenuously)
  1556. ! to <obj> from <oldloc> to <newloc>.  Returns the number of the first 
  1557. ! object (even if there is more than one) being carried along.  The <q>
  1558. ! argument is optional; if true, the objects are moved quietly, i.e.
  1559. ! without a message being printed.
  1560.  
  1561. routine MoveAllAttachables(obj, oldloc, newloc, q)
  1562. {
  1563.     local first, second
  1564.  
  1565.     obj is not workflag
  1566.     
  1567. ! First, move all attachables in the specified object:
  1568.     
  1569.     first = Move_Attachables(obj, oldloc, newloc)
  1570.  
  1571. ! Then, move anything else in the old location that might somehow be
  1572. ! attached to it:
  1573.  
  1574.     second = Move_Attachedto(obj, oldloc, newloc)
  1575.  
  1576.     if not first:  first = second
  1577.  
  1578.     if first and not q
  1579.         ! "With it (attached) to..."
  1580.         OMessage(attachable, 21, first)
  1581.     return first
  1582. }
  1583.  
  1584.  
  1585. ! Move_Attachables moves all objects attached by an attachable item in <obj>.
  1586.  
  1587. routine Move_Attachables(obj, oldloc, newloc)
  1588. {
  1589.     local i, k, first
  1590.     
  1591.     for i in obj
  1592.     {
  1593.         k = Move_AttachedObjects(i, obj, oldloc, newloc)
  1594.         if not first:  first = k
  1595.     }
  1596.     return first
  1597. }
  1598.  
  1599.  
  1600. ! Move_AttachedObjects is called to move everything attached to the 
  1601. ! attachable <att> in <oldloc> to <newloc>.
  1602.  
  1603. routine Move_AttachedObjects(att, obj, oldloc, newloc)
  1604. {
  1605.     local i, j, first
  1606.  
  1607.     if att.#attached_to
  1608.     {
  1609.         for (i=1; i<=att.#attached_to; i++)
  1610.         {
  1611.             j = att.attached_to #i
  1612.  
  1613.             if j
  1614.             {
  1615.                 if Contains(oldloc, j) and j not in obj
  1616.                 {
  1617.                     if obj is not workflag
  1618.                     {
  1619.                         first = att
  1620.                         obj is workflag
  1621.                     }
  1622.                     move j to newloc
  1623.                     j is moved
  1624.                     Move_Attachables(j, oldloc, newloc)
  1625.                     Move_Attachedto(j, oldloc, newloc)
  1626.                 }
  1627.             }
  1628.         }
  1629.     }
  1630.     return first
  1631. }
  1632.  
  1633.  
  1634. ! Move_Attachedto scans <oldloc> for anything attached to <obj>, moving
  1635. ! any valid matches to <newloc>.  Not particularly graceful, since it must
  1636. ! recurse inward for each object, since attachments are explicit only from
  1637. ! attachable to object, and not vice-versa.
  1638.  
  1639. routine Move_Attachedto(obj, oldloc, newloc)
  1640. {
  1641.     local i, j, k, first
  1642.  
  1643.     for i in oldloc
  1644.     {
  1645.         for (j=1; j<=i.#attached_to; j++)
  1646.         {
  1647.             if i.attached_to #j = obj or
  1648.                 Contains(obj, i.attached_to #j):
  1649.             {
  1650.                 move i to newloc
  1651.                 i is moved
  1652.                 if not first:  first = i
  1653.                 k=Move_AttachedObjects(i, obj, oldloc, newloc)
  1654.                 if not first:  first = k
  1655.                 k=Move_Attachables(i, obj, oldloc, newloc)
  1656.                 if not first:  first = k
  1657.             }
  1658.         }
  1659.         
  1660.         if child(i)
  1661.         {
  1662.             k = Move_Attachedto(obj, child(i), newloc)
  1663.             if not first:  first = k
  1664.         }
  1665.     }
  1666.     return first
  1667. }
  1668.  
  1669.  
  1670. ! ListAttachments prints everything attached to the attachable <obj>.
  1671. ! Basically an adaptation of PropertyList from HUGOLIB.H.
  1672.  
  1673. routine ListAttachments(obj)
  1674. {
  1675.     local a, b, n, total
  1676.  
  1677.     for (a=1; a<=obj.#attached_to; a++)
  1678.     {
  1679.         if obj.attached_to #a:  total++
  1680.     }
  1681.     
  1682.     for (a=1; a<=obj.#attached_to; a++)
  1683.     {
  1684.         b = obj.attached_to #a
  1685.         if b
  1686.         {
  1687.             The(b)
  1688.             if Contains(player, b)
  1689.                 OMessage(attachable, 22)  ! " you're holding"
  1690.             if a < total and total > 2
  1691.                 print ", ";
  1692.             elseif a = total - 1:  print " ";
  1693.  
  1694.             if a = total - 1
  1695.                 print "and ";
  1696.             n++
  1697.         }
  1698.     }
  1699.     return n
  1700. }
  1701.  
  1702. #endif  ! ifset USE_ATTACHABLES
  1703.  
  1704.  
  1705. !----------------------------------------------------------------------------
  1706. ! OBJECT LIBRARY MESSAGES
  1707. !
  1708. ! OMessage and NewOMessages mirror HUGOLIB.H's Message and NewMessages,
  1709. ! respectively, although not exactly.  The difference is that the first
  1710. ! argument passed to OMessage (or NewOMessages) is an object or class, 
  1711. ! not a routine address.
  1712. !
  1713. ! OMessage(object, number)
  1714. ! prints message <number> for object/class <object>
  1715. !
  1716. ! OMessage (object, number, var1, var2)
  1717. ! where one or two variables--which may be objects or any other value--
  1718. ! are used by message <number> for object/class <object>
  1719.  
  1720. routine OMessage(obj, num, a, b)
  1721. {
  1722.     ! Check first to see if the NewOMessages routine provides a 
  1723.     ! replacement message:
  1724.     !
  1725.     if NewOMessages(obj, num, a, b):  return
  1726.  
  1727.     select obj
  1728.  
  1729.     case door
  1730.     {
  1731.         select num
  1732.         case 1
  1733.         {
  1734.             print CThe(player); " will have to get ";
  1735.             if parent(player).prep #2
  1736.                 print parent(player).prep #2; " ";
  1737.             else
  1738.                 print "out ";
  1739.             print "of "; The(parent(player)); " first."
  1740.         }
  1741.         case 2:  print "(opening "; The(self); " first)"
  1742.         case 3:  print CThe(self); IsorAre(self, true); " locked."
  1743.         case 4:  print CThe(self); IsorAre(self, true); " closed."
  1744.         case 5
  1745.         {
  1746.             CThe(actor)
  1747.             print " open"; MatchSubject(actor); " "; \
  1748.             The(self); " and ";
  1749.  
  1750.             if actor in location
  1751.                 print "leave"; MatchSubject(actor);
  1752.             elseif location = self.between #((parent(actor) = \
  1753.                 self.between #1) + 1)
  1754.                 print "come"; MatchSubject(actor); " in";
  1755.             print ", closing it behind "; actor.pronoun #2;
  1756.             if actor is plural
  1757.                 print "selves."
  1758.             else
  1759.                 print "self."
  1760.         }
  1761.         case 6:  print CThe(self); IsorAre(self, true); " open."
  1762.         case 7:  print CThe(self); IsorAre(self, true); " closed."
  1763.     }
  1764.  
  1765.     case component:  print CThe(player); " can't separate "; \
  1766.                 The(self); " from "; The(self.part_of); "."
  1767.  
  1768. #ifset USE_VEHICLES
  1769.     case vehicle        
  1770.     {
  1771.         select num
  1772.         case 1
  1773.         {
  1774.              print "To walk, "; CThe(player); " will have to
  1775.                 get "; self.prep #2; " of "; The(self); \
  1776.                 ".  Otherwise, try \""; self.vehicle_verbs; \
  1777.                 "\" ";
  1778.              if self.#vehicle_verbs > 1
  1779.                 print "or \""; self.vehicle_verbs #2; "\" ";
  1780.              print "and a direction."
  1781.         }
  1782.         case 2:  print CThe(player); IsorAre(player); \
  1783.                 " not in anything at the moment."
  1784.         case 3:  print CThe(player); IsorAre(player); " "; \
  1785.             a.prep; " "; The(a);"--try \""; a.vehicle_verbs; "\"."
  1786.         case 4:  print "You'll have to specify a direction as well."
  1787.         case 5:  print CArt(obstacle); " stop"; \
  1788.                 MatchSubject(obstacle); " "; \
  1789.                 The(player, true); " from going anywhere."
  1790.         case 6
  1791.         {
  1792.             print CThe(player); " can't "; a.vehicle_verbs; " ";
  1793.             if object.prep
  1794.                 print object.prep;
  1795.             else
  1796.                 print "in";
  1797.             print " "; The(object); "."
  1798.         }
  1799.         case 7:  print CThe(player); " can't "; a.vehicle_verbs; \
  1800.             " that way."
  1801.     }
  1802. #endif  ! ifset USE_VEHICLES
  1803.  
  1804. #ifset USE_PLURAL_OBJECTS
  1805.     case plural_class
  1806.     {
  1807.         select num
  1808.         case 1
  1809.         {
  1810.             if pluralobj_heldmode = 1
  1811.                 print CThe(player); \
  1812.                 MatchPlural(player, "doesn't", "don't"); \
  1813.                 " have any ";
  1814.             else
  1815.                 print "There are no ";
  1816.             print self.noun; " to "; word[1]; "."
  1817.         }
  1818.         case 2
  1819.         {
  1820.             if pluralobj_heldmode = 1
  1821.                 print CThe(player); " only"; \
  1822.                     MatchPlural(player, "has", "have");
  1823.             else
  1824.             {
  1825.                 print "There ";
  1826.                 if a = 1:  print "is";
  1827.                 else:  print "are";
  1828.                 print " only ";
  1829.             }
  1830.             print NumberWord(a); " ";
  1831.             if a > 1
  1832.                 print self.noun;
  1833.             else
  1834.                 print self.single_noun;
  1835.             print " to "; word[1]; "."
  1836.         }
  1837.         case 3:  print CThe(player); " will have to do that one "; \
  1838.                 self.single_noun; " at a time."
  1839.     }
  1840. #endif  ! ifset USE_PLURAL_OBJECTS
  1841.  
  1842. #ifset USE_ATTACHABLES
  1843.     case attachable
  1844.     {
  1845.         select num
  1846.         case 1
  1847.         {
  1848.             print CArt(self); IsorAre(self, true); " "; \
  1849.                 self.attached_desc; " ";
  1850.             ListAttachments(self)
  1851.             print "."
  1852.         }
  1853.         case 2:  print CThe(player); " see"; MatchSubject(player); \
  1854.             " nothing special about "; The(self); "."
  1855.         case 3
  1856.         {
  1857.             print CThe(self); IsorAre(self, true); " "; \
  1858.                 self.attached_desc; " ";
  1859.             ListAttachments(self)
  1860.             print "."
  1861.         }
  1862.         case 4:  print CThe(player); " will have to "; \
  1863.             self.detach_verb; " before you can take it."
  1864.         case 5:  print "(having to "; self.detach_verb; " "; \
  1865.             The(self); " "; self.detach_prep; " "; \
  1866.             The(self.attached_to #a); " first)"
  1867.         case 6:  print CThe(player); " can't "; word[1]; " "; \
  1868.             The(object); "."
  1869.         case 7:  print "You might want to try \""; a; "\" with "; \
  1870.             The(object); " instead of "; "\""; word[1]; "\"."
  1871.         case 8:
  1872.         {
  1873.             print "You'll have to be a little more specific
  1874.                 about what you'd like ";
  1875.             if player_person ~= 2:  print The(player, true); " ";
  1876.             print "to "; object.attach_verb; " "; \
  1877.             The(object); " "; object.attach_prep; "."
  1878.         }
  1879.         case 9:  print "(taking "; The(object); " first)"
  1880.         case 10:  print CThe(player); " can't "; word[1]; " "; \
  1881.             The(object); " "; object.attach_prep; " "; \
  1882.             The(xobject); "."
  1883.         case 11:  print "Except that "; The(object); \
  1884.             IsorAre(object, true); " already "; \
  1885.             object.attached_desc; " "; The(xobject); "."
  1886.         case 12
  1887.         {
  1888.             print "Except that "; The(object); " is already "; \
  1889.             object.attached_desc; " "; 
  1890.             ListAttachments(object)
  1891.             print "."
  1892.         }
  1893.         case 13:  print CThe(player); " "; object.attach_verb; \
  1894.             MatchSubject(player); " "; The(object); " "; \
  1895.             object.attach_prep; " "; The(xobject); "."
  1896.         case 14:  print "Except that "; The(object); \
  1897.             IsorAre(object, true); " not "; \
  1898.             object.attached_desc; " "; The(xobject); "."
  1899.         case 15:  print "Except that "; The(object); \
  1900.             IsorAre(object, true); " not "; \
  1901.             object.attached_desc; " anything."
  1902.         case 16:
  1903.         {
  1904.             print "You'll have to be a little more specific
  1905.                 about what you'd like ";
  1906.             if player_person ~= 2:  print The(player, true); " ";
  1907.             print "to "; word[1]; " "; The(object); " "; \
  1908.                 object.detach_prep; "."
  1909.         }
  1910.         case 17:  print CThe(player); " "; object.detach_verb; \
  1911.             MatchSubject(player); " "; The(object); " "; \
  1912.             object.detach_prep; " "; The(xobject); "."
  1913.         case 18
  1914.         {
  1915.             print CThe(player); " can't go anywhere while "; \
  1916.                 The(a); IsorAre(a, true); " still "; \
  1917.                 a.attached_desc; " ";
  1918.             ListAttachments(a)
  1919.             print "."
  1920.         }
  1921.         case 19
  1922.         {
  1923.             print CThe(player); " can't move whatever is "; \
  1924.             a.attached_desc; " "; The(a); ", namely ";
  1925.             ListAttachments(a)
  1926.             print "."
  1927.         }
  1928.         case 20:  print CThe(player); " can't pull "; The(a); \
  1929.             " that way."
  1930.         case 21
  1931.         {
  1932.             print "(with "; The(a); " "; a.attached_desc; " "; 
  1933.             ListAttachments(a) 
  1934.             print ")"
  1935.         }
  1936.         case 22:  print " "; The(player); IsorAre(player); " holding";
  1937.     }
  1938. #endif  ! ifset USE_ATTACHABLES
  1939. }
  1940.  
  1941. routine NewOMessages(obj, num, a, b)    ! The NewOMessages routine may be
  1942. {                                       ! REPLACED, and should return true
  1943.     return false                    ! if a replacement message <num>
  1944. }                                       ! exists for object/class <obj>
  1945.  
  1946. #endif  ! ifset _COMPILING_HUGOLIB
  1947.